#include "url.h"
#include <algorithm>

namespace crawler { namespace server {
namespace {
std::string NormalizePath(const std::string src) {
  std::string dest;
  int i=0, seg_src=0;
  size_t base_pos = 0;
  assert(src[0] == '/');
  while (i < src.size()) {
    if (src[i] == '/') {
      if (i-seg_src == 2 && src[seg_src] == '.' && src[seg_src+1] == '.') {
        size_t pos = dest.rfind('/');
        if (pos > base_pos) {
          pos = dest.rfind('/', pos - 1);
        }
        dest.erase(pos);
      } else if (i-seg_src == 1 && src[seg_src] == '.') {
        size_t pos = dest.rfind('/');
        dest.erase(pos);
      }
      dest.push_back(src[i++]);
      seg_src = i;
    } else {
      dest.push_back(src[i++]);
    }
  }
  return dest;
}
}
boost::regex Url::s_url_regex_("(http)://((.+?)(:(.+?))?@)?(.+?)(:(.+?))?"
                               "(/.*?)?(\\?(.*?)?)?(#(.+)?)?", boost::regex::icase);
boost::regex Url::s_path_regex_("(/(.*/)?)(.*?(\\.(.*))?)?");
Url::Url(const std::string &url) : url_(url) {
  boost::smatch url_what;
  valid_ = regex_match(url, url_what, s_url_regex_);
  if (!valid_) return;

  // 1      3    5   6    8    9    11    13
  // scheme user pwd host port path query ref
  size_t url_idx[] = {1,3,5,6,8,9,11,13};
  std::string *url_segs[] = {&scheme_, &user_, &password_, &host_,
                             &port_, &path_, &query_, &ref_};
  for (size_t i=0; i<sizeof(url_idx)/sizeof(url_idx[0]); ++i) {
    const size_t idx = url_idx[i];
    url_segs[i]->assign(url_what[idx].first, url_what[idx].second);
  }
  if (path_.empty()) path_ = "/";
  ToLower(scheme_);
  ToLower(host_);

  boost::smatch path_what;
  valid_ = regex_match(path_, path_what, s_path_regex_);
  if (!valid_) return;

  // 1          3     5
  // directory  file  suffix
  size_t path_idx[] = {1,3,5};
  std::string *path_segs[] = {&directory_, &file_, &file_suffix_};
  for (size_t i=0; i<sizeof(path_idx)/sizeof(path_idx[0]); ++i) {
    const size_t idx = path_idx[i];
    path_segs[i]->assign(path_what[idx].first, path_what[idx].second);
  }

  // normalize
  normalized_.reserve(url_.size()*2);
  normalized_ += scheme_ + "://";
  if (!user_.empty()) {
    normalized_ += user_;
    if (!password_.empty()) {
      normalized_ += ":" + password_;
    }
    normalized_ += "@";
  }
  normalized_ += host_;
  if (!port_.empty() && port_ != "80") {
    normalized_ += ":" + port_;
  }
  normalized_ += NormalizePath(path_);
  if (!query_.empty()) {
    normalized_ += "?" + query_;
  }

  // depth
  depth_ = std::count(path_.begin(), path_.end(), '/') - 1;
}
std::string Url::GetHrefLink(const std::string &href) {
  if (href.empty()) return std::string();

  std::string link;
  if (href[0] == '/') {
    link = scheme_ + "://" + host_ + href;
  } else if (href.find("http") == 0) {
    link = href;
  } else {
    link = scheme_ + "://" + host_ + directory_ + href;
  }

  Url n(link);
  if (!n.IsValid()) return std::string();
  return n.GetNormalized();
}
}}

